/* Copyright (c) 2010, mitsuhisa.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of the misuhisa.net nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include <cstdlib>
#include "Tree.hpp"
#include "Handle.hpp"
#include "Block.hpp"
#include "String.hpp"
#include "StrPool.hpp"
#include "Symtbl.hpp"
#include "GeneralObject.hpp"
#include "Array.hpp"
#include "Range.hpp"

namespace nyu {

namespace parser {

Tree::~Tree() {
}

Value Tree::evalPut(Value v, Context& c, bool is_tail) {
    // DUMMY
    return NULL_VALUE;
}

Value Tree::evalSet(Value v, Context& c, bool is_tail) {
    // DUMMY
    return NULL_VALUE;
}

Value Tree::evalIAdd(Value v, Context& c, bool is_tail) {
    // DUMMY
    return NULL_VALUE;
}

Value Tree::evalIncr(Context& c, bool is_tail) {
    // DUMMY
    return NULL_VALUE;
}

bool Tree::evalBool(Context& c, bool is_tail) {
    Handle<Value> v(eval(c, is_tail));
    return v.get() & ~1;
}

TreeList::TreeList(Tree* value, TreeList* next) : value_(value), next_(next) {
}

TreeList::~TreeList() {
    delete value_;

    TreeList* l = next_;
    while (l) {
        TreeList* next = l->next_;
        l->next_ = 0;

        delete l;

        l = next;
    }
}

Value TreeList::eval(Context& c, bool is_tail) {
    TreeList* l = this;

    for (; l->next_; l = l->next_) {
        make_handle(l->value_->eval(c));
    }

    return l->value_->eval(c, true);
}

TreeUnit::TreeUnit(TreeList* stmts) : stmts_(stmts) {
}

TreeUnit::~TreeUnit() {
    delete stmts_;
}

Value TreeUnit::eval(Context& c, bool is_tail) {
    TreeList* l = stmts_;

    if (!l)
        return NULL_VALUE;

    for (; l->next(); l = l->next()) {
        make_handle(l->value()->eval(c));
    }

    return l->value()->eval(c);
}

TreeExpr1::TreeExpr1(Tree* node) : node_(node) {
}

TreeExpr1::~TreeExpr1() {
    delete node_;
}

Value TreeIncr::eval(Context& c, bool is_tail) {
    return node_->evalIncr(c, is_tail);
}

TreeExpr2::TreeExpr2(Tree* lnode, Tree* rnode) :
        lnode_(lnode), rnode_(rnode) {
}

TreeExpr2::~TreeExpr2() {
    delete lnode_;
    delete rnode_;
}

#define NYU_IMPL_EXPR2(NAME,OP) \
Value Tree##NAME::eval(Context& c, bool is_tail) { \
    Value v1 = lnode_->eval(c); \
    Value v2 = rnode_->eval(c); \
 \
    if (is_object(v1)) { \
        Handle<Object> o1(value_to_object(v1)); \
        return o1->op##NAME(v2, c); \
    } \
 \
    if (is_object(v2)) { \
        Handle<Object> o2(value_to_object(v2)); \
        v2 = o2->toInt(c); \
    } \
 \
    return v1 OP v2; \
}

NYU_IMPL_EXPR2(Add,+);
NYU_IMPL_EXPR2(Sub,-);
NYU_IMPL_EXPR2(Mod,%);
NYU_IMPL_EXPR2(Or,|);
NYU_IMPL_EXPR2(Xor,^);
NYU_IMPL_EXPR2(And,&);

#undef NYU_IMPL_EXPR2

#define NYU_IMPL_EXPR2_2(NAME,OP) \
Value Tree##NAME::eval(Context& c, bool is_tail) { \
    Value v1 = lnode_->eval(c); \
    Value v2 = rnode_->eval(c); \
 \
    if (is_object(v1)) { \
        Handle<Object> o1(value_to_object(v1)); \
        return o1->op##NAME(v2, c); \
    } \
 \
    if (is_object(v2)) { \
        Handle<Object> o2(value_to_object(v2)); \
        v2 = o2->toInt(c); \
    } \
 \
    return v1 OP value_to_int(v2); \
}

NYU_IMPL_EXPR2_2(Mul,*);
NYU_IMPL_EXPR2_2(Div,/);
NYU_IMPL_EXPR2_2(LShift,<<);

#undef NYU_IMPL_EXPR2_2

Value TreeRShift::eval(Context& c, bool is_tail) {
    Value v1 = lnode_->eval(c);
    Value v2 = rnode_->eval(c);

    if (is_object(v1)) {
        Handle<Object> o1(value_to_object(v1));
        return o1->opRShift(v2, c);
    }

    if (is_object(v2)) {
        Handle<Object> o2(value_to_object(v2));
        v2 = o2->toInt(c);
    }

    return (v1 >> value_to_int(v2)) & ~1;
}

Value TreeExpr2Bool::eval(Context& c, bool is_tail) {
    return int_to_value(evalBool(c, is_tail));
}

#define NYU_IMPL_EXPR2B(NAME,OP) \
bool Tree##NAME::evalBool(Context& c, bool is_tail) { \
    Value v1 = lnode_->eval(c); \
    Value v2 = rnode_->eval(c); \
 \
    if (is_object(v1)) { \
        Handle<Object> o1(value_to_object(v1)); \
        Handle<Value> vv2(v2); \
        return o1->op##NAME(v2, c); \
    } \
 \
    if (is_object(v2)) { \
        Handle<Object> o2(value_to_object(v2)); \
        v2 = o2->toInt(c); \
    } \
 \
    return v1 OP v2; \
}

NYU_IMPL_EXPR2B(Eq,==);
NYU_IMPL_EXPR2B(Ne,!=);
NYU_IMPL_EXPR2B(Lt,<);
NYU_IMPL_EXPR2B(Le,<=);
NYU_IMPL_EXPR2B(Gt,>);
NYU_IMPL_EXPR2B(Ge,>=);

#undef NYU_IMPL_EXPR2B

bool TreeOwnProto::evalBool(Context& c, bool is_tail) {
    return false;
}

bool TreeOwnMember::evalBool(Context& c, bool is_tail) {
    return false;
}

Value TreePut::eval(Context& c, bool is_tail) {
    Value v2 = rnode_->eval(c);
    return lnode_->evalPut(v2, c, is_tail);
}

Value TreeSet::eval(Context& c, bool is_tail) {
    Value v2 = rnode_->eval(c);
    return lnode_->evalSet(v2, c, is_tail);
}

Value TreeIAdd::eval(Context& c, bool is_tail) {
    Value v2 = rnode_->eval(c);
    return lnode_->evalIAdd(v2, c, is_tail);
}

TreeCond::TreeCond(Tree* cond, Tree* tnode, Tree* fnode) :
        cond_(cond), tnode_(tnode), fnode_(fnode) {
}

TreeCond::~TreeCond() {
    delete cond_;
    delete tnode_;
    delete fnode_;
}

Value TreeCond::eval(Context& c, bool is_tail) {
    return (cond_->evalBool(c) ? tnode_ : fnode_)->eval(c, is_tail);
}

TreeCall::TreeCall(Tree* expr, TreeList* args) : expr_(expr), args_(args) {
}

TreeCall::~TreeCall() {
    delete expr_;
    delete args_;
}

Value TreeCall::eval(Context& c, bool is_tail) {
    Value val = expr_->eval(c);

    if (is_int(val)) {
        std::cerr << "int is not callable" << std::endl;
        return NULL_VALUE;
    }
    if (is_null(val)) {
        std::cerr << "null is not callable" << std::endl;
        return NULL_VALUE;
    }

    if (is_tail) {
        return updateScope(c, val);
    }

    Scope* scope = c.newScope();

    {
        Value target = c.currentTarget();
        if (is_object(target) && !is_null(target))
            value_to_object(target)->ref();
        scope->argv(0, target);
    }

    int argc = 1;
    for (TreeList* l = args_; l; l = l->next(), argc++) {
        scope->argv(argc, l->value()->eval(c));
    }
    scope->argc(argc);
    scope->block(val);

    c.pushScope(scope);

    call_val:

    assert(is_object(val) && !is_null(val));

    {
        Handle<Object> obj(value_to_object(val));
        val = obj->opCall(c);
    }

    if (c.isUpdated()) {
        c.setIsUpdated(false);
        goto call_val;
    }

    c.popScope();

    return val;
}

Value TreeCall::updateScope(Context& c, const Value& val) {
    Value tmp_argv[NYU_MAX_ARGS];

    {
        Value target = c.currentTarget();
        if (is_object(target) && !is_null(target))
            value_to_object(target)->ref();
        tmp_argv[0] = target;
    }

    int argc = 1;
    for (TreeList* l = args_; l; l = l->next(), argc++) {
        tmp_argv[argc] = l->value()->eval(c);
    }

    Scope* scope = c.currentScope();
    scope->clear();

    for (int i = 0; i < argc; i++) {
        scope->argv(i, tmp_argv[i]);
    }
    scope->argc(argc);
    scope->block(val);

    c.setIsUpdated(true);

    return val;
}

TreeSymbol::TreeSymbol(char* symbol) :
        symbol_(StrPool::get(symbol)), symidx_(Symtbl::getIndex(symbol_)) {
}

TreeSymbol::~TreeSymbol() {
    symbol_->unref();
}

Value TreeSymbol::eval(Context& c, bool is_tail) {
    Symref* ref = c.getSymref(symidx_);
    if (!ref) {
        std::cerr << "variable is not defined: name=" << symbol_->strval()
                << std::endl;
        return NULL_VALUE;
    }

    Value v = ref->value;
    if (is_object(v) && !is_null(v))
        value_to_object(v)->ref();

    return v;
}

Value TreeSymbol::evalPut(Value v, Context& c, bool is_tail) {
    if (is_object(v) && !is_null(v)) {
        value_to_object(v)->ref();
    }

    Symref* ref = c.getSymref(symidx_);
    if (!ref || ref->scope != c.currentScope()) {
        ref = c.putSymref(symidx_, v);
        return v;
    }

    Value& vv = ref->value;
    if (is_object(vv) && !is_null(vv)) {
        value_to_object(vv)->unref();
    }

    return vv = v;
}

Value TreeSymbol::evalSet(Value v, Context& c, bool is_tail) {
    if (is_object(v) && !is_null(v)) {
        value_to_object(v)->ref();
    }

    Symref* ref = c.getSymref(symidx_);
    if (!ref) {
        ref = c.putSymref(symidx_, v);
        return v;
    }

    Value& vv = ref->value;
    if (is_object(vv) && !is_null(vv)) {
        value_to_object(vv)->unref();
    }

    return vv = v;
}

Value TreeSymbol::evalIAdd(Value v, Context& c, bool is_tail) {
    Symref* ref = c.getSymref(symidx_);
    if (!ref) {
        // TODO
        return v;
    }

    Value& vv = ref->value;
    if (is_object(vv)) {
        if (is_null(vv)) {
            // TODO exception
            return vv = v;
        }

        // TODO
        if (is_object(v) && !is_null(v)) {
            value_to_object(v)->ref();
        }

        value_to_object(vv)->unref();

        return vv = v;
    }

    if (is_object(v)) {
        v = is_null(v) ? 0 : value_to_object(v)->toInt(c);
    }

    return vv += v;
}

Value TreeSymbol::evalIncr(Context& c, bool is_tail) {
    Symref* ref = c.getSymref(symidx_);
    if (!ref) {
        std::cerr << "variable is not defined: name=" << symbol_->strval()
                << std::endl;
        return NULL_VALUE;
    }

    Value& v = ref->value;
    if (is_object(v)) {
        return NULL_VALUE;
    }

    return v += int_to_value(1);
}

TreeInt::TreeInt(int_type value) : value_(value) {
}

Value TreeInt::eval(Context& c, bool is_tail) {
    return int_to_value(value_);
}

TreeFloat::TreeFloat(float_type value) : value_(value) {
}

Value TreeFloat::eval(Context& c, bool is_tail) {
    // TODO
    return NULL_VALUE;
}

TreeStr::TreeStr(char* value) : value_(StrPool::get(value)) {
}

TreeStr::~TreeStr() {
    value_->unref();
}

Value TreeStr::eval(Context& c, bool is_tail) {
    value_->ref();
    return object_to_value(value_);
}

TreeBlock::TreeBlock(TreeList* stmts) : block_(new Block(stmts)) {
}

TreeBlock::~TreeBlock() {
    block_->unref();
}

Value TreeBlock::eval(Context& c, bool is_tail) {
    block_->ref();
    return object_to_value(block_);
}

TreeObject::TreeObject(TreeList* slots) : slots_(slots) {
}

TreeObject::~TreeObject() {
    delete slots_;
}

Value TreeObject::eval(Context& c, bool is_tail) {
    Symref* ref = c.getSymref(Symtbl::getIndex(String::OBJECT));
    assert(ref);

    Object* obj = new GeneralObject(ref->value);

    for (TreeList* l = slots_; l; l = l->next()) {
        TreeObjectSlot* slot = static_cast<TreeObjectSlot*> (l->value());
        String* name = slot->name();
        name->ref();
        Value val = slot->value()->eval(c);
        obj->putSlot(object_to_value(name), val, c);
    }

    return object_to_value(obj);
}

TreeObjectSlot::TreeObjectSlot(char* name, Tree* value) :
        name_(StrPool::get(name)), value_(value) {
}

TreeObjectSlot::~TreeObjectSlot() {
    name_->unref();
    delete value_;
}

Value TreeObjectSlot::eval(Context& c, bool is_tail) {
    // nothing to do
    return 0;
}

TreeArray::TreeArray(TreeList* elms) :
        elms_(elms), nelms_(elms ? elms->length() : 0) {
}

TreeArray::~TreeArray() {
    delete elms_;
}

Value TreeArray::eval(Context& c, bool is_tail) {
    Symref* ref = c.getSymref(Symtbl::getIndex(String::ARRAY));
    assert(ref);

    Array* array = new Array(ref->value, nelms_);

    size_t i = 0;
    for (TreeList* l = elms_; l; l = l->next(), i++) {
        (*array)[i] = l->value()->eval(c);
    }

    return object_to_value(array);
}

TreeRange::TreeRange(bool is_in, Tree* start, Tree* end, Tree* step) :
        is_in_(is_in), start_(start), end_(end), step_(step) {
}

TreeRange::~TreeRange() {
    delete start_;
    delete end_;
    delete step_;
}

Value TreeRange::eval(Context& c, bool is_tail) {
    Symref* ref = c.getSymref(Symtbl::getIndex(String::RANGE));
    assert(ref);

    Value start = start_->eval(c);
    Value end = end_->eval(c);
    Value step = step_ ? step_->eval(c) : int_to_value(1);

    Range* range = new Range(ref->value, is_in_, start, end, step);

    return object_to_value(range);
}

TreeSlotAccess::TreeSlotAccess(Tree* expr, char* name) :
        expr_(expr), name_(StrPool::get(name)) {
}

TreeSlotAccess::~TreeSlotAccess() {
    delete expr_;
    name_->unref();
}

Value TreeSlotAccess::eval(Context& c, bool is_tail) {
    Value v1 = expr_->eval(c);

    if (!is_object(v1) || is_null(v1)) {
        std::cerr << "int or null has no slots!" << std::endl;
        return NULL_VALUE;
    }

    c.currentTarget(v1);

    Value ret = value_to_object(v1)->getSlot(object_to_value(name_), c);
    if (is_object(ret) && !is_null(ret))
        value_to_object(ret)->ref();

    return ret;
}

Value TreeSlotAccess::evalPut(Value v, Context& c, bool is_tail) {
    Handle<Value> v1(expr_->eval(c));

    if (!is_object(v1.get()) || is_null(v1.get())) {
        std::cerr << "int or null has no slots!" << std::endl;
        return v;
    }

    name_->ref();

    if (is_object(v) && !is_null(v)) {
        value_to_object(v)->ref();
    }

    v1->putSlot(object_to_value(name_), v, c);

    return v;
}

Value TreeSlotAccess::evalIAdd(Value v, Context& c, bool is_tail) {
    Handle<Value> v1(expr_->eval(c));

    if (!is_object(v1.get()) || is_null(v1.get())) {
        std::cerr << "int or null has no slots!" << std::endl;
        return v;
    }

    Handle<Value> vv(v);

    return v1->opIAdd(object_to_value(name_), v, c, is_tail);
}

TreeItemAccess::TreeItemAccess(Tree* expr, Tree* idx) :
        expr_(expr), idx_(idx) {
}

TreeItemAccess::~TreeItemAccess() {
    delete expr_;
    delete idx_;
}

Value TreeItemAccess::eval(Context& c, bool is_tail) {
    Value v1 = expr_->eval(c);

    if (!is_object(v1) || is_null(v1)) {
        std::cerr << "int or null has no slots!" << std::endl;
        return NULL_VALUE;
    }

    c.currentTarget(v1);

    Handle<Value> idx(idx_->eval(c));

    Value ret = value_to_object(v1)->opGetItem(idx.get(), c);
    if (is_object(ret) && !is_null(ret))
        value_to_object(ret)->ref();

    return ret;
}

TreeArgref::TreeArgref(int index) :
    index_(index) {
}

Value TreeArgref::eval(Context& c, bool is_tail) {
    Value val = c.currentArgv(index_);
    if (is_object(val) && !is_null(val))
        value_to_object(val)->ref();
    return val;
}

TreeArglen::TreeArglen() {
}

Value TreeArglen::eval(Context& c, bool is_tail) {
    return int_to_value(c.currentArgc());
}

TreeCurblk::TreeCurblk() {
}

Value TreeCurblk::eval(Context& c, bool is_tail) {
    Value v = c.currentBlock();
    if (is_object(v) && !is_null(v))
        value_to_object(v)->ref();
    return v;
}

#define NYU_DUMMY_IMPL(NAME) \
Value Tree##NAME::eval(Context& c, bool is_tail) { \
    return NULL_VALUE; \
}

NYU_DUMMY_IMPL(ISub);
NYU_DUMMY_IMPL(IMul);
NYU_DUMMY_IMPL(IDiv);
NYU_DUMMY_IMPL(IMod);

NYU_DUMMY_IMPL(ILShift);
NYU_DUMMY_IMPL(IRShift);
NYU_DUMMY_IMPL(IAnd);
NYU_DUMMY_IMPL(IXor);
NYU_DUMMY_IMPL(IOr);
NYU_DUMMY_IMPL(ILAnd);
NYU_DUMMY_IMPL(ILOr);
NYU_DUMMY_IMPL(LOr);
NYU_DUMMY_IMPL(LXor);
NYU_DUMMY_IMPL(LAnd);
NYU_DUMMY_IMPL(Cmp);

NYU_DUMMY_IMPL(Not);
NYU_DUMMY_IMPL(Invert);
NYU_DUMMY_IMPL(Pos);
NYU_DUMMY_IMPL(Neg);
NYU_DUMMY_IMPL(Decr);
NYU_DUMMY_IMPL(PostIncr);
NYU_DUMMY_IMPL(PostDecr);

#undef NYU_DUMMY_IMPL

}

}
